﻿using System;
using System.Collections.Generic;
using System.Text;
using Tourreau.Gilles.FaxDotNet;
using System.IO;
using System.Threading;
using log4net;

namespace FaxServerNET
{
    public class FaxCore : IDisposable
    {
        #region Properties
        public string FaxServerName { get; set; }
        public IFaxRelayer FaxRelayer { get; set; }
        public int RunEverySeconds { get; set; }
        public string PrinterName { get; set; }
        public string ReceivedFaxesFolderPath
        {
            get
            {
                return _watcher.Path;
            }
            set
            {
                _watcher.Path = value;


                //Enable the FileSystemWatcher events.
                _watcher.EnableRaisingEvents = true;
            }
        }

        #endregion

        #region Fields
        private static readonly ILog _log = LogManager.GetLogger(typeof(FaxCore));
        private List<string> _files_to_process;
        private FileSystemWatcher _watcher;
        private FaxServer _fax_server;
        private List<FileToFax> _files_to_fax;

        private Timer _timer;
        #endregion

        public FaxCore()
        {
            //Create a new FileSystemWatcher.
            _watcher = new FileSystemWatcher();

            //Subscribe to the Created event.
            _watcher.Created += new
            FileSystemEventHandler(OnFaxReceived);

            //Set the path to C:\Temp\

            //Initialize Windows' Fax
            _files_to_fax = new List<FileToFax>();
            _files_to_process = new List<string>();
        }

        public void RunServer()
        {
             _timer = new Timer(new TimerCallback(UpdateServer), null, 1000, RunEverySeconds * 1000);
        }

        //private bool _running_update = false;
        public void UpdateServer(object o)
        {
            lock (_files_to_fax)
            {
                _log.Debug("Try Relay Received Faxes ...");
                TryRelayReceivedFaxes();
                _log.Debug("Send faxes ...");
                SendFaxes();
            }
        }


        void OnFaxReceived(object sender, FileSystemEventArgs e)
        {
            //A new .TXT file has been created in C:\Temp\
            _files_to_process.Add(e.FullPath);
            TryRelayReceivedFaxes();
        }

        private void TryRelayReceivedFaxes()
        {
            string[] list_of_files_to_process = new string[_files_to_process.Count];
            _files_to_process.CopyTo(list_of_files_to_process);
            foreach (var file_path in list_of_files_to_process)
            {
                if (IsFileReady(file_path))
                {
                    //send the file
                    _files_to_process.Remove(file_path);
                    FaxRelayer.SendFaxReceived(file_path);
                }
            }
        }

        /// <summary>
        /// Checks if the fax received is ready to be relayed
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private bool IsFileReady(string path)
        {
            try
            {
                using (FileStream file_received = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None))
                {
                }
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }


        /// <summary>
        /// Sends all the faxes that need to be sent. Faxes needed to be sent are 
        /// retrived from the FaxRelayer.
        /// </summary>
        public void SendFaxes()
        {
            _files_to_fax.AddRange( FaxRelayer.GetFilesToFax());

            FileToFax[] files_to_fax = _files_to_fax.ToArray();
            foreach (var file_to_fax in files_to_fax)
            {
                if (SendFax("", file_to_fax))
                   _files_to_fax.Remove(file_to_fax);
            }
        }

        private bool SendFax(string recipient, FileToFax file_to_fax)
        {
            bool fax_sent = false;
            FaxInformations info;
            FaxJob job;

            _log.DebugFormat("Connecting to {0}@{1}",PrinterName,FaxServerName);
            using (_fax_server = new FaxServer(FaxServerName, PrinterName))
            {
                info = _fax_server.GetDefaultInformations();  //Get default data informations on Windows Fax Service

                info.RecipientName = recipient; //Fill the recipient name
                info.RecipientNumber = file_to_fax.FaxNumber;  

                // Console.WriteLine("Going to send : " + Path.GetFullPath(filename));
                try
                {
                    _log.InfoFormat("Creating Fax Job for fax to {2}-[{1}], from {0}", file_to_fax.SenderAddress, file_to_fax, file_to_fax.FaxNumber);
                    job = _fax_server.Send(info, Path.GetFullPath(file_to_fax.FilePath));

                    FaxRelayer.SendFaxStatus(file_to_fax, string.Format("Fax Job[{0}] created for {1} to {2} at {3}.\n\n" +
                                                                        "Wait for Fax Delivery Receipt or check your Fax Server@{4}",
                                                                        job.ID, 
                                                                        Path.GetFileName(file_to_fax.FilePath), 
                                                                        file_to_fax.FaxNumber , 
                                                                        DateTime.Now.ToString(),
                                                                        FaxServerName));
                }
                catch (Exception e)
                {
                    _log.Error(e);
                    FaxRelayer.SendFaxStatus(file_to_fax, string.Format("FAILED creating Fax Job {0} to {1} at {2}. \n" +
                                                                        "Please make sure you have a valid fax number and/or try a different format for your file (such as a PDF) and try sending it again.\n" +
                                                                        " More info about the error: {3}", 
                                                                        Path.GetFileName(file_to_fax.FilePath), 
                                                                        file_to_fax.FaxNumber, 
                                                                        DateTime.Now.ToString(), 
                                                                        e.ToString()));
                }
                fax_sent = true;
            }
            return fax_sent;
        }

        protected virtual void Dispose(bool dispose)
        {
            if (_fax_server != null)
                if (dispose)
                {
                    _fax_server.Dispose();
                    _fax_server = null;
                }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        ~FaxCore()
        {
            Dispose(false);
        }



        public void StopServer()
        {
            if (_timer != null)
                _timer.Dispose();
        }
    }
}
